package edu.hawaii.jmotif.sax.datastructures;

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * The motif records collection.
 * 
 * @author Pavel Senin
 * 
 */
public class MotifRecords implements Iterable<MotifRecord> {

  /** Default capacity. */
  private static final Integer defaultCapacity = 10;

  /** Storage container. */
  private final LinkedList<MotifRecord> motifs;

  // max capacity var
  private Integer maxCapacity;

  /**
   * Constructor.
   */
  public MotifRecords() {
    this.maxCapacity = defaultCapacity;
    this.motifs = new LinkedList<MotifRecord>();
  }

  /**
   * Constructor.
   * 
   * @param capacity The initial capacity.
   */
  public MotifRecords(int capacity) {
    this.maxCapacity = capacity;
    this.motifs = new LinkedList<MotifRecord>();
  }

  /**
   * Set the max capacity of this collection. The overflow elements will be pushed into the
   * collection pushing out ones with less distance value.
   * 
   * @param newSize The size to set.
   */
  public void setMaxCapacity(int newSize) {
    this.maxCapacity = newSize;
  }

  /**
   * Add a new motif to the list. Here is a trick. This method will also check if the current
   * distance is less than best so far (best in the table). If so - there is no need to continue
   * that inner loop - the MAGIC optimization.
   * 
   * @param motif The motif instance to add.
   * @return if the motif got added.
   */
  public boolean add(MotifRecord motif) {

    // System.out.println(" + discord record " + discord);

    // check if here is still a room for the new element
    //
    if (this.motifs.size() < this.maxCapacity) {
      if (this.motifs.size() == 0) {
        assert true;
      }
      // if so - just add it in
      this.motifs.add(motif);
    }
    else {
      // more complicated - need to check if it will fit in there
      // DiscordRecord last = discords.get(discords.size() - 1);
      MotifRecord first = motifs.get(0);
      if (first.getFrequency() < motif.getFrequency()) {
        // System.out.println(" - discord record " + discords.get(0));
        motifs.remove(0);
        motifs.add(motif);
      }
    }
    Collections.sort(motifs);
    if (this.motifs.get(0).compareTo(motif) > 0) {
      return true;
    }
    return false;
  }

  /**
   * Returns the number of the top hits.
   * 
   * @param num The number of instances to return. If the number larger than the storage size -
   * returns the storage as is.
   * @return the top motif hits.
   */
  public List<MotifRecord> getTopHits(Integer num) {
    Collections.sort(this.motifs);
    if (num >= this.motifs.size()) {
      Collections.reverse(this.motifs);
      return this.motifs;
    }
    List<MotifRecord> res = this.motifs.subList(this.motifs.size() - num, this.motifs.size());
    Collections.reverse(res);
    return res;
  }

  /**
   * Get the minimal distance found among all instances in the collection.
   * 
   * @return The minimal distance found among all instances in the collection.
   */
  public double getMinDistance() {
    if (this.motifs.size() > 0) {
      return motifs.get(0).getFrequency();
    }
    return -1;
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder(1024);
    for (MotifRecord r : motifs) {
      sb.append(r.getFrequency()).append(" at ");
      sb.append(Arrays.toString(r.getPositions())).append("\n");
    }
    return sb.toString();
  }

  @Override
  public Iterator<MotifRecord> iterator() {
    return this.motifs.iterator();
  }
}
